// 云函数入口文件
const cloud = require('wx-server-sdk')
const JSSHA = require('jssha')
const base32Decode = require('base32-decode')

cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
const _ = db.command

// 定义TOTP集合名称
const TOTP_CODES_COLLECTION = 'totpCodes'
const TOTP_ACCOUNTS_COLLECTION = 'totpAccounts'

// 生成TOTP验证码
const generateTOTPCode = (secret) => {
  try {
    // 移除空格和换行符
    const sanitizedSecret = secret.replace(/\s/g, '').toUpperCase()

    // 解码Base32
    const decoded = base32Decode(sanitizedSecret, 'RFC4648')

    // 计算当前时间戳
    const epoch = Math.floor(Date.now() / 1000)
    let timeCounter = Math.floor(epoch / 30)

    // 转换为字节数组
    const timeByteArray = new Uint8Array(8)
    for (let i = 0; i < 8; i++) {
      timeByteArray[7 - i] = timeCounter & 0xff
      timeCounter = timeCounter >> 8
    }

    // 使用HMAC-SHA1计算哈希
    const shaObj = new JSSHA('SHA-1', 'ARRAYBUFFER')
    shaObj.setHMACKey(decoded, 'ARRAYBUFFER')
    shaObj.update(timeByteArray.buffer)
    const hmac = shaObj.getHMAC('ARRAYBUFFER')

    // 根据RFC获取截断索引
    const offset = new Uint8Array(hmac)[19] & 0xf

    // 从哈希中提取4字节
    const bytes = new Uint8Array(hmac)
    const binary =
      ((bytes[offset] & 0x7f) << 24) |
      ((bytes[offset + 1] & 0xff) << 16) |
      ((bytes[offset + 2] & 0xff) << 8) |
      (bytes[offset + 3] & 0xff)

    // 获取6位数字的TOTP码
    const otp = binary % 1000000

    // 确保是6位数字（前导零）
    return otp.toString().padStart(6, '0')
  } catch (error) {
    console.error('生成TOTP码失败:', error)
    return null
  }
}

// 保存验证码到数据库
const saveTOTPCodesToDB = async (codes, timestamp) => {
  try {
    // 清理已有的验证码（避免重复）
    const bulkClear = Object.keys(codes).map((accountId) => {
      return db.collection(TOTP_CODES_COLLECTION).where({ accountId }).remove()
    })
    await Promise.all(bulkClear)

    // 计算精确的本周期结束时间
    const periodEnd = Math.ceil(timestamp / 30) * 30

    const bulkOps = Object.entries(codes).map(([accountId, code]) => ({
      accountId,
      code,
      timestamp,
      periodStart: Math.floor(timestamp / 30) * 30, // 本周期开始时间
      periodEnd, // 本周期结束时间
      expiresAt: periodEnd // 使用周期结束时间作为过期时间
    }))

    console.log(`保存${bulkOps.length}个验证码，当前时间戳: ${timestamp}, 周期结束时间: ${periodEnd}`)

    // 批量写入数据库
    const promises = bulkOps.map((data) => db.collection(TOTP_CODES_COLLECTION).add({ data }))

    await Promise.all(promises)
    return true
  } catch (error) {
    console.error('保存TOTP码到数据库失败:', error)
    return false
  }
}

// 从数据库获取验证码
const getTOTPCodesFromDB = async (accountIds) => {
  try {
    // 查询当前有效的验证码
    const currentTime = Math.floor(Date.now() / 1000)

    // 使用云函数特性查询 - 查找过期时间大于当前时间的验证码
    const { data } = await db
      .collection(TOTP_CODES_COLLECTION)
      .where({
        accountId: _.in(accountIds),
        expiresAt: _.gt(currentTime)
      })
      .get()

    if (data && data.length > 0) {
      console.log(`从数据库获取到${data.length}个有效验证码`)

      const codes = {}
      const timestamps = {}
      const expiresAts = {}

      data.forEach((item) => {
        codes[item.accountId] = item.code
        timestamps[item.accountId] = item.timestamp
        expiresAts[item.accountId] = item.expiresAt
      })

      return {
        codes,
        timestamps,
        expiresAts
      }
    }
    return null
  } catch (error) {
    console.error('从数据库获取TOTP码失败:', error)
    return null
  }
}

// 获取账户信息
const getAccountsFromDB = async (accountIds) => {
  try {
    const { data } = await db
      .collection(TOTP_ACCOUNTS_COLLECTION)
      .where({
        id: _.in(accountIds)
      })
      .get()

    console.log(`按ID查询到${data.length}个账户，查询条件:`, accountIds)
    return data
  } catch (error) {
    console.error('获取账户信息失败:', error)
    return []
  }
}

// 获取用户的所有账户
const getUserAccounts = async (userId) => {
  try {
    const { data } = await db
      .collection(TOTP_ACCOUNTS_COLLECTION)
      .where({
        userId
      })
      .get()

    console.log(`用户${userId}有${data.length}个账户`)
    return data
  } catch (error) {
    console.error('获取用户账户失败:', error)
    return []
  }
}

// 云函数入口
exports.main = async (event, context) => {
  const { action, data } = event
  const { OPENID } = cloud.getWXContext()

  console.log(`执行操作: ${action}`, data, `OPENID: ${OPENID}`)

  // 根据操作类型分发处理
  switch (action) {
    case 'getTimeInfo': {
      // 获取服务器时间信息
      const currentTime = Math.floor(Date.now() / 1000)
      const periodStart = Math.floor(currentTime / 30) * 30
      const periodEnd = periodStart + 30
      const remaining = periodEnd - currentTime

      console.log(`提供时间同步信息: 当前时间 ${currentTime}, 周期结束 ${periodEnd}, 剩余 ${remaining}秒`)

      return {
        success: true,
        data: {
          currentTime,
          periodStart,
          periodEnd,
          remaining
        }
      }
    }

    case 'clearAccounts': {
      // 清除用户的所有TOTP账户
      try {
        // 1. 先获取用户的所有账户ID
        const userAccounts = await getUserAccounts(OPENID)
        const accountIds = userAccounts.map((acc) => acc.id)

        console.log(`准备清除用户 ${OPENID} 的 ${accountIds.length} 个账户`)

        // 2. 删除所有相关的验证码记录
        if (accountIds.length > 0) {
          await db
            .collection(TOTP_CODES_COLLECTION)
            .where({
              accountId: _.in(accountIds)
            })
            .remove()
          console.log(`验证码记录已清除`)
        }

        // 3. 删除所有账户记录
        await db
          .collection(TOTP_ACCOUNTS_COLLECTION)
          .where({
            userId: OPENID
          })
          .remove()

        return {
          success: true,
          message: '所有账户数据已清除',
          deletedCount: accountIds.length
        }
      } catch (error) {
        console.error('清除账户失败:', error)
        return {
          success: false,
          error: '清除账户失败',
          message: error.message
        }
      }
    }

    case 'generate': {
      // 生成新的TOTP验证码
      const { accountIds, newAccount } = data

      if (!accountIds || !Array.isArray(accountIds)) {
        return { error: '需要提供账户ID数组' }
      }

      // 如果提供了新账户信息，保存到数据库
      if (newAccount) {
        try {
          console.log('保存新账户信息:', newAccount)
          await db.collection(TOTP_ACCOUNTS_COLLECTION).add({
            data: {
              ...newAccount,
              userId: OPENID,
              createTime: db.serverDate()
            }
          })
          console.log('新账户保存成功')
        } catch (error) {
          console.error('保存新账户失败:', error)
        }
      }

      // 获取账户信息
      const accounts = await getAccountsFromDB(accountIds)
      console.log('从数据库获取到的账户:', accounts)

      const accountMap = {}
      accounts.forEach((acc) => {
        accountMap[acc.id] = acc
      })

      const codes = {}
      const timestamp = Math.floor(Date.now() / 1000)
      const periodEnd = Math.ceil(timestamp / 30) * 30 // 本周期结束时间

      // 为每个账户生成验证码
      for (const accountId of accountIds) {
        // 优先使用从数据库获取的账户信息
        let account = accountMap[accountId]

        // 如果数据库中找不到但有新账户信息，则使用新账户信息
        if (!account && newAccount && newAccount.id === accountId) {
          console.log('使用新提供的账户信息:', newAccount)
          account = newAccount
        }

        if (account && account.secret) {
          const code = generateTOTPCode(account.secret)
          if (code) {
            codes[accountId] = code
            console.log(`为账户 ${account.name} (${accountId}) 生成验证码: ${code}`)
          } else {
            codes[accountId] = '错误'
            console.error(`为账户 ${account.name} (${accountId}) 生成验证码失败`)
          }
        } else {
          codes[accountId] = '账户不存在'
          console.error(`账户不存在: ${accountId}`)
        }
      }

      // 保存到数据库
      await saveTOTPCodesToDB(codes, timestamp)

      return {
        success: true,
        data: {
          codes,
          timestamp,
          periodStart: Math.floor(timestamp / 30) * 30,
          periodEnd,
          expiresAt: periodEnd
        }
      }
    }

    case 'getCurrentCodes': {
      // 获取当前有效的验证码
      const { accountIds, newAccount } = data

      if (!accountIds || !Array.isArray(accountIds)) {
        return { error: '需要提供账户ID数组' }
      }

      // 如果提供了新账户，确保它已保存
      if (newAccount) {
        try {
          console.log('保存新账户信息:', newAccount)
          // 检查是否已存在
          const existCheck = await db.collection(TOTP_ACCOUNTS_COLLECTION).where({ id: newAccount.id }).count()

          if (existCheck.total === 0) {
            await db.collection(TOTP_ACCOUNTS_COLLECTION).add({
              data: {
                ...newAccount,
                userId: OPENID,
                createTime: db.serverDate()
              }
            })
            console.log('新账户保存成功')
          }
        } catch (error) {
          console.error('保存或检查新账户失败:', error)
        }
      }

      // 先从数据库获取
      const dbCodes = await getTOTPCodesFromDB(accountIds)
      let codes = dbCodes ? dbCodes.codes : {}
      const timestamps = dbCodes ? dbCodes.timestamps : {}
      const expiresAts = dbCodes ? dbCodes.expiresAts : {}

      // 处理缺失的验证码
      const missingAccountIds = accountIds.filter((id) => !codes[id])
      console.log('缺失验证码的账户IDs:', missingAccountIds)

      if (missingAccountIds.length > 0) {
        // 获取账户信息
        const accounts = await getAccountsFromDB(missingAccountIds)
        console.log('从数据库获取到的账户:', accounts)

        const accountMap = {}
        accounts.forEach((acc) => {
          accountMap[acc.id] = acc
        })

        // 为缺失的账户生成新验证码
        const newCodes = {}
        const timestamp = Math.floor(Date.now() / 1000)
        const periodEnd = Math.ceil(timestamp / 30) * 30 // 本周期结束时间

        for (const accountId of missingAccountIds) {
          // 优先使用从数据库获取的账户信息
          let account = accountMap[accountId]

          // 如果数据库中找不到但有新账户信息，则使用新账户信息
          if (!account && newAccount && newAccount.id === accountId) {
            console.log('使用新提供的账户信息:', newAccount)
            account = newAccount
          }

          if (account && account.secret) {
            const code = generateTOTPCode(account.secret)
            if (code) {
              newCodes[accountId] = code
              timestamps[accountId] = timestamp
              expiresAts[accountId] = periodEnd
              console.log(`为账户 ${account.name} (${accountId}) 生成验证码: ${code}`)
            } else {
              newCodes[accountId] = '错误'
              console.error(`为账户 ${account.name} (${accountId}) 生成验证码失败`)
            }
          } else {
            newCodes[accountId] = '账户不存在'
            console.error(`账户不存在: ${accountId}`)
          }
        }

        // 保存新生成的验证码
        if (Object.keys(newCodes).length > 0) {
          await saveTOTPCodesToDB(newCodes, timestamp)

          // 合并结果
          codes = {
            ...codes,
            ...newCodes
          }
        }
      }

      return {
        success: true,
        data: {
          codes,
          timestamps,
          expiresAts,
          currentTime: Math.floor(Date.now() / 1000)
        }
      }
    }

    case 'syncAccounts': {
      // 同步账户信息
      const { accounts } = data

      if (!accounts || !Array.isArray(accounts)) {
        return { error: '需要提供账户数组' }
      }

      try {
        console.log(`开始同步${accounts.length}个账户`)

        // 使用事务批量更新账户信息
        const promises = accounts.map((account) => {
          console.log(`处理账户: ${account.name} (${account.id})`)
          return db
            .collection(TOTP_ACCOUNTS_COLLECTION)
            .where({ id: account.id })
            .count()
            .then((res) => {
              console.log(`账户 ${account.id} 查询结果: ${res.total}`)
              if (res.total === 0) {
                // 不存在则添加
                console.log(`添加新账户: ${account.name}`)
                return db.collection(TOTP_ACCOUNTS_COLLECTION).add({
                  data: {
                    ...account,
                    userId: OPENID,
                    createTime: db.serverDate()
                  }
                })
              } else {
                // 存在则更新
                console.log(`更新现有账户: ${account.name}`)
                return db
                  .collection(TOTP_ACCOUNTS_COLLECTION)
                  .where({ id: account.id })
                  .update({
                    data: {
                      name: account.name,
                      secret: account.secret,
                      issuer: account.issuer,
                      updateTime: db.serverDate()
                    }
                  })
              }
            })
        })

        await Promise.all(promises)
        console.log('账户同步完成')

        return {
          success: true,
          message: '账户同步成功'
        }
      } catch (error) {
        console.error('同步账户信息失败:', error)
        return { error: '同步账户信息失败' }
      }
    }

    case 'getUserAccounts': {
      // 获取用户所有账户
      try {
        const accounts = await getUserAccounts(OPENID)
        console.log(`获取到用户账户数量: ${accounts.length}`)

        return {
          success: true,
          data: { accounts }
        }
      } catch (error) {
        console.error('获取用户账户失败:', error)
        return { error: '获取用户账户失败' }
      }
    }

    case 'deleteAccount': {
      // 删除账户
      const { accountId } = data

      if (!accountId) {
        return { error: '需要提供账户ID' }
      }

      try {
        console.log(`删除账户: ${accountId}`)

        // 先检查账户是否存在
        const accountCheck = await db
          .collection(TOTP_ACCOUNTS_COLLECTION)
          .where({
            id: accountId,
            userId: OPENID // 确保只能删除自己的账户
          })
          .get()

        if (accountCheck.data.length === 0) {
          console.log(`账户不存在或无权限删除: ${accountId}`)
          return {
            success: false,
            error: '账户不存在或无权限删除'
          }
        }

        // 删除账户
        await db
          .collection(TOTP_ACCOUNTS_COLLECTION)
          .where({
            id: accountId,
            userId: OPENID
          })
          .remove()

        // 删除关联的验证码
        await db
          .collection(TOTP_CODES_COLLECTION)
          .where({
            accountId
          })
          .remove()

        console.log(`账户已删除: ${accountId}`)
        return {
          success: true,
          message: '账户已删除'
        }
      } catch (error) {
        console.error('删除账户失败:', error)
        return { error: '删除账户失败' }
      }
    }

    default:
      return { error: '未知操作类型' }
  }
}
